package cn.mothin.frame.core.db;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cn.mothin.frame.core.interfaces.vo.IQueryPage;
import cn.mothin.frame.util.SqlUtil;

/**
 * JpaDAO基础类
 * 
 * @author zhoush
 * 
 * @param <T>
 * @param <PK>
 */
public class GenericDao<T, PK extends Serializable> {
	protected final Log logger = LogFactory.getLog(super.getClass());
	protected Class<T> entityClass;
	@PersistenceContext
	private EntityManager entityManager;

	@SuppressWarnings({ "unchecked" })
	public GenericDao() {
		entityClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public void save(T entity) {
		if (entity == null) {
			return;
		}
		entityManager.persist(entity);
	}

	@SuppressWarnings("unchecked")
	public void save(List<T> entities) {
		if (entities == null) {
			return;
		}

		if (entities.size() > 0) {
			for (Object entity : entities) {
				save((T) entity);
			}
		}
	}

	public void delete(T entity) {
		if (entity == null) {
			return;
		}
		entityManager.remove(entity);
	}

	public void delete(PK id) {
		if (id == null) {
			return;
		}
		delete(get(id));
	}

	@SuppressWarnings("unchecked")
	public void delete(Object... PKs) {
		if (PKs != null && PKs.length > 0) {
			for (Object id : PKs) {
				delete(get((PK) id));
			}
		}
	}

	@SuppressWarnings("unchecked")
	public void delete(List<T> entities) {
		if (entities != null && entities.size() > 0) {
			for (Object entity : entities) {
				delete((T) entity);
			}
		}
	}

	public void clear() {
		String hql = "DELETE " + entityClass.getName();
		entityManager.createQuery(hql).executeUpdate();
	}

	public void update(T entity) {
		if (entity == null) {
			return;
		}
		entityManager.persist(entity);
	}

	@SuppressWarnings("unchecked")
	public void update(List<T> entities) {
		if (entities != null && entities.size() > 0) {
			for (Object entity : entities) {
				update((T) entity);
			}
		}
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		String hql = "FROM " + entityClass.getName();
		return entityManager.createQuery(hql).getResultList();
	}

	public T get(PK id) {
		return entityManager.find(entityClass, id);
	}

	private Query createQuery(String queryString, Object... values) {
		Query query = entityManager.createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; ++i) {
				query.setParameter(i+1, values[i]);
			}
		}
		return query;
	}

	public int execute(final String hql, final Object... values) {
		Query query = createQuery(hql, values);
		return query.executeUpdate();
	}

	@SuppressWarnings("rawtypes")
	public List find(final String hql, final Object... values) {
		return createQuery(hql, values).getResultList();
	}

	private String modifyHQL(IQueryPage page, String hql) {
		String newHQL = hql;
		if (page.getSort() != null && page.getSort().length() > 0
				&& !page.getSort().contains(";")) {
			newHQL = SqlUtil.removeOrders(newHQL);
			newHQL += " ORDER BY " + page.getSort();
			newHQL += " ";
			newHQL += page.getOrder().equals(IQueryPage.ASC) ? IQueryPage.ASC
					: IQueryPage.DESC;
			return newHQL;
		}
		return newHQL;
	}

	@SuppressWarnings("rawtypes")
	public void find(IQueryPage page, String hql, final Object... values) {
		String countQueryString = "SELECT COUNT(*) "
				+ SqlUtil.removeSelect(SqlUtil.removeOrders(hql));
		page.setTotal(findLong(countQueryString, values).intValue());

		final int first = page.getFirst();
		final int pageSize = page.getRows();
		final String newHQL = modifyHQL(page, hql);
		Query query = createQuery(newHQL, values);
		query.setFirstResult(first);
		query.setMaxResults(pageSize);
		List list = query.getResultList();
		page.setResult(list);
	}

	public Object findUnique(final String hql, final Object... values) {
		Query query = createQuery(hql, values);
		return query.getSingleResult();
	}

	public Integer findInt(String hql, Object... values) {
		return (Integer) findUnique(hql, values);
	}

	public Long findLong(String hql, Object... values) {
		return (Long) findUnique(hql, values);
	}

	@SuppressWarnings("unchecked")
	public T findUniqueByProperty(String propertyName, Object value) {
		String hql = "FROM " + entityClass.getName() + " WHERE " + propertyName
				+ " = ?";
		return (T) findUnique(hql, value);
	}

	public int executeJdbcSaveOrUpdate(String sql, Object... objects) {
		Query query = entityManager.createNativeQuery(sql);
		if (objects != null) {
			for (int i = 0; i < objects.length; i++) {
				query.setParameter(i+1, objects[i]);
			}
		}
		return query.executeUpdate();
	}
}
